package com.platform.common.handler;

import com.platform.common.bean.BaseException;
import com.platform.common.bean.Result;
import com.platform.common.handler.exception.ExceptionResult;
import com.platform.common.handler.exception.ExceptionResultFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

/**
 * describe:全局异常处理器
 *
 * @author Administrator
 * @date 2019/12/15
 * @Time 11:04
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 应用到所有@RequestMapping注解方法，在其执行之前初始化数据绑定器
     * 当有参数时才执行该方法,多少个参数就执行多少次
     * 可用于数据转换等
     *
     * @param binder WebDataBinder
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        log.info("当前key：" + binder.getObjectName());
    }

    /**
     * 把值绑定到Model中，使全局@RequestMapping可以获取到该值
     *
     * @param model 模版
     */
    @ModelAttribute
    public void addAttributes(Model model) {
//        model.addAttribute("author", "嘟嘟MD");
    }

    /**
     * 全局异常捕捉处理
     *
     * @param ex 错误
     * @return 页面或者统一返回的格式
     */
    @ExceptionHandler(value = Exception.class)
    public Object errorHandler(Exception ex, HttpServletRequest req) {
        log.error("全局异常捕捉处理", ex.getMessage());
        //策略模式+简单工厂模式
        ExceptionResult exceptionResult = ExceptionResultFactory.getResult(ex);
        return exceptionResult.retResult();
    }

    /**
     * Servlet请求错误异常捕捉
     * @param ex
     * @param req
     * @return
     */
    @ExceptionHandler(value = ServletException.class)
    public Object errorServletHandler(ServletException ex,HttpServletRequest req){
        log.info("Servlet请求错误异常捕捉");
        log.error("Servlet请求错误异常捕捉",ex.getMessage());
        log.error("Servlet请求路径",req.getRequestURI());
        if (isAjax(req)) {
            log.error("ajax请求错误", ex.getMessage());
            //策略模式+简单工厂模式
            ExceptionResult exceptionResult = ExceptionResultFactory.getResult(ex);
            return exceptionResult.retResult();
        } else {
            //页面请求错误
            log.error("网页错误", ex.getMessage());
            return new ModelAndView("redirect:/login");
        }
    }

    /**
     *HttpMessage错误异常捕捉
     * @param ex
     * @param req
     * @return
     */
    @ExceptionHandler(value = HttpMessageConversionException.class)
    public Object errorHttpMessageHandler(HttpMessageConversionException ex,HttpServletRequest req){
        log.error("HttpMessage请求错误异常捕捉", ex.getMessage());
        log.error("HttpMessage请求路径",req.getRequestURI());
        ExceptionResult exceptionResult = ExceptionResultFactory.getResult(ex);
        return exceptionResult.retResult();
    }

    /**
     * 运行时错误异常捕捉
     * @param ex
     * @param req
     * @return
     */
    @ExceptionHandler(value = RuntimeException.class)
    public Object errorRuntimeHandler(RuntimeException ex,HttpServletRequest req){
        log.error("运行时错误异常捕捉", ex.getMessage());
        ExceptionResult exceptionResult = ExceptionResultFactory.getResult(ex);
        return exceptionResult.retResult();
    }

    /**
     * 自定义异常处理
     *
     * @param ex 错误
     * @return 统一错误返回
     */
    @ExceptionHandler(value = BaseException.class)
    public Object myErrorHandler(BaseException ex) {
        log.error("自定义异常错误", ex.getMessage());
        return Result.failure(ex.getCode(), ex.getMessage());
    }

    /**
     * 判断是否是ajax请求
     *
     * @param req HttpServletRequest
     * @return 是ajax请求 true
     */
    private boolean isAjax(HttpServletRequest req) {
        log.info("判断是否是ajax请求");
        //使用HttpServletRequest中的header检测请求是否为ajax, 如果是ajax则返回json, 如果为非ajax则返回view(即ModelAndView)
        String contentTypeHeader = req.getHeader("Content-Type");
        String acceptHeader = req.getHeader("Accept");
        String xRequestedWith = req.getHeader("x-requested-with");

        boolean isAjax =
                (contentTypeHeader != null && contentTypeHeader.contains(MediaType.APPLICATION_JSON_VALUE)) ||
                        (acceptHeader != null && (acceptHeader.contains(MediaType.APPLICATION_JSON_VALUE) || "*/*".equalsIgnoreCase(acceptHeader))) ||
                        "XMLHttpRequest".equalsIgnoreCase(xRequestedWith);
        return isAjax;
    }

}
